﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Redbrick.Silverlight.Common;

namespace Moria.Common.Model
{
	public class CaveFactory 
	{

		#region Consts & Settings


		public const int COST_ADJ = 100;
		public const int MAX_DUNGEON_OBJ = 344;
		public const int MAX_OBJ_LEVEL = 50;     // Maximum Level of magic in dungeon     

		const int MIN_MALLOC_TD = 4;/* Number of people on town level (day)  */
		const int MIN_MALLOC_TN = 8; /* Number of people on town level (night)*/

		// Dungeon generation values					
		// Note: The entire design of dungeon can be changed by only	 
		//	 slight adjustments here.				 
		const int DUN_TUN_RND = 9;		// 1/Chance of Random direction	       
		const int DUN_TUN_CHG = 70;	// Chance of changing direction (99 max) 
		const int DUN_TUN_CON = 15;	// Chance of extra tunneling	       
		const int DUN_ROO_MEA = 32;	// Mean of # of rooms, standard dev2     
		const int DUN_TUN_PEN = 25;	// % chance of room doors		       
		const int DUN_TUN_JCT = 15;	// % chance of doors at tunnel junctions 
		const int DUN_STR_DEN = 5;		// Density of streamers		       
		const int DUN_STR_RNG = 2;		// TileWidth of streamers		       
		const int DUN_STR_MAG = 3;		// Number of magma streamers	       
		const int DUN_STR_MC = 90;		// 1/y chance of treasure per magma      
		const int DUN_STR_QUA = 2;		// Number of quartz streamers	       
		const int DUN_STR_QC = 40;		// 1/y chance of treasure per quartz     
		const int DUN_UNUSUAL = 300;	// Level/y chance of unusual room	       

		#endregion Consts & Settings

		#region Declarations

		private Stack<Point> doorstk = new Stack<Point>();
		private int cur_width { get { return this.cave.Width; } }
		private int cur_height { get { return this.cave.Height; } }
		private int dun_level { get { return this.cave.Level; } }
		public bool IsDebug { get; set; }
		public DungeonLevel cave { get; private set; }

		#endregion Declarations

		#region Constructor


		#endregion Constructor

		#region Methods

		#region Room Generation

		private void RoomAt(FloorType floor, int top, int lft, int btm, int rgt)
		{
			for (int y = top; y <= btm; y++)
			{
				for (int x = lft; x <= rgt; x++)
				{
					CaveFloor c_ptr = this.cave[y, x];
					c_ptr.FloorType = floor;
					c_ptr.IsRoom = true;
				}
			}

			for (int y = (top - 1); y <= (btm + 1); y++)
			{
				CaveFloor c_ptr = this.cave[y, lft - 1];
				c_ptr.FloorType = FloorType.GraniteWall;
				c_ptr.IsRoom = true;
				c_ptr = this.cave[y, rgt + 1];
				c_ptr.FloorType = FloorType.GraniteWall;
				c_ptr.IsRoom = true;
			}

			for (int x = lft; x <= rgt; x++)
			{
				CaveFloor c_ptr = this.cave[top - 1, x];
				c_ptr.FloorType = FloorType.GraniteWall;
				c_ptr.IsRoom = true;
				c_ptr = this.cave[btm + 1, x];
				c_ptr.FloorType = FloorType.GraniteWall;
				c_ptr.IsRoom = true;
			}
		}

		/// <summary>
		///  Builds a room at a row, column coordinate
		/// </summary>
		private void build_room(Point pt)
		{
			FloorType floor = FloorType.DarkFloor;
			if (dun_level <= Dice.RandInt(25))
				floor = FloorType.LightFloor;	// Floor with light	


			int top = pt.y - Dice.RandInt(4);
			int btm = pt.y + Dice.RandInt(3);
			int lft = pt.x - Dice.RandInt(11);
			int rgt = pt.x + Dice.RandInt(11);

			this.RoomAt(floor, top, lft, btm, rgt);
		}
		/// <summary>
		///  ItemType 1 unusual rooms are several overlapping rectangular ones
		/// </summary>
		private void build_type1(Point pt)
		{
			FloorType floor = FloorType.DarkFloor;
			if (dun_level <= Dice.RandInt(25))
				floor = FloorType.LightFloor;	// Floor with light	

			int limit = 1 + Dice.RandInt(2);

			for (int ndx = 0; ndx < limit; ndx++)
			{
				int top = pt.y - Dice.RandInt(4);
				int btm = pt.y + Dice.RandInt(3);
				int lft = pt.x - Dice.RandInt(11);
				int rgt = pt.x + Dice.RandInt(11);

				RoomAt(floor, top, lft, btm, rgt);
			}
		}
		///<summary>
		/// ItemType 2 unusual rooms all have an inner room:			
		///   1 - Just an inner room with one door			
		///   2 - An inner room within an inner room			
		///   3 - An inner room with pillar(s)				
		///   4 - Inner room has a maze					
		///   5 - A set of four inner rooms		
		///</summary>
		private void build_type2(Point pt)
		{
			Debug.WriteLine("build_type2()");
			FloorType floor = FloorType.DarkFloor;
			if (dun_level <= Dice.RandInt(25))
				floor = FloorType.LightFloor;	// Floor with light	

			int top = pt.y - 4;
			int btm = pt.y + 4;
			int lft = pt.x - 11;
			int rgt = pt.x + 11;

			this.RoomAt(floor, top, lft, btm, rgt);

			// The inner room		
			top = top + 2;
			btm = btm - 2;
			lft = lft + 2;
			rgt = rgt - 2;

			for (int y = (top - 1); y <= (btm + 1); y++)
			{
				this.cave[y, lft - 1].FloorType = FloorType.TempWall1;
				this.cave[y, rgt + 1].FloorType = FloorType.TempWall1;
			}

			for (int x = lft; x <= rgt; x++)
			{
				this.cave[top - 1, x].FloorType = FloorType.TempWall1;
				this.cave[btm + 1, x].FloorType = FloorType.TempWall1;
			}

			// Inner room variations		
			switch (Dice.RandInt(5))
			{
				case 1:
					{
						Debug.WriteLine("Just an inner room");
						// Just an inner room.	
						int tmp = Dice.RandInt(4);
						if (tmp < 3)
						{	// Place a door	
							if (tmp == 1) this.cave.place_secret_door(top - 1, pt.x);
							else this.cave.place_secret_door(btm + 1, pt.x);
						}
						else
						{
							if (tmp == 3) this.cave.place_secret_door(pt.y, lft - 1);
							else this.cave.place_secret_door(pt.y, rgt + 1);
						}
						vault_monster(pt, 1);
						break;
					}
				case 2:
					{
						Debug.WriteLine("Treasure Vault");
						// Treasure Vault	
						int tmp = Dice.RandInt(4);
						if (tmp < 3)
						{	// Place a door	
							if (tmp == 1) this.cave.place_secret_door(top - 1, pt.x);
							else this.cave.place_secret_door(btm + 1, pt.x);
						}
						else
						{
							if (tmp == 3) this.cave.place_secret_door(pt.y, lft - 1);
							else this.cave.place_secret_door(pt.y, rgt + 1);
						}

						for (int y = pt.y - 1; y <= pt.y + 1; y++)
						{
							this.cave[y, pt.x - 1].FloorType = FloorType.TempWall1;
							this.cave[y, pt.x + 1].FloorType = FloorType.TempWall1;
						}

						this.cave[pt.y - 1, pt.x].FloorType = FloorType.TempWall1;
						this.cave[pt.y + 1, pt.x].FloorType = FloorType.TempWall1;

						tmp = Dice.RandInt(4);	// Place a door	
						if (tmp < 3) this.cave.place_locked_door(pt.y - 3 + (tmp << 1), pt.x); // 1 . Point.y-1; 2 . Point.y+1
						else this.cave.place_locked_door(pt.y, pt.x - 7 + (tmp << 1));

						// Place an object in the treasure vault	
						tmp = Dice.RandInt(10);
						if (tmp > 2) this.cave.place_object(pt);
						else if (tmp == 2) this.cave.place_down_stairs(pt);
						else this.cave.place_up_stairs(pt);

						// Guard the treasure well		
						vault_monster(pt, 2 + Dice.RandInt(3));
						// If the monsters don't get 'em.	
						vault_trap(pt, 4, 10, 2 + Dice.RandInt(3));
						break;
					}
				case 3:
					{
						Debug.WriteLine("Inner pillar(s)");
						// Inner pillar(s).	
						int tmp = Dice.RandInt(4);
						if (tmp < 3)
						{	// Place a door	
							if (tmp == 1) this.cave.place_secret_door(top - 1, pt.x);
							else this.cave.place_secret_door(btm + 1, pt.x);
						}
						else
						{
							if (tmp == 3) this.cave.place_secret_door(pt.y, lft - 1);
							else this.cave.place_secret_door(pt.y, rgt + 1);
						}

						for (int y = pt.y - 1; y <= pt.y + 1; y++)
						{
							for (int x = pt.x - 1; x <= pt.x + 1; x++)
							{
								this.cave[y, x].FloorType = FloorType.TempWall1;
							}
						}

						if (Dice.RandInt(2) == 1)
						{
							tmp = Dice.RandInt(2);
							for (int y = pt.y - 1; y <= pt.y + 1; y++)
							{
								for (int x = pt.x - 5 - tmp; x <= pt.x - 3 - tmp; x++)
								{
									this.cave[y, x].FloorType = FloorType.TempWall1;
								}
							}
							for (int y = pt.y - 1; y <= pt.y + 1; y++)
							{
								for (int x = pt.x + 3 + tmp; x <= pt.x + 5 + tmp; x++)
								{
									this.cave[y, x].FloorType = FloorType.TempWall1;
								}
							}
						}

						if (Dice.RandInt(3) == 1)	// Inner rooms	
						{
							for (int x = pt.x - 5; x <= pt.x + 5; x++)
							{
								this.cave[pt.y - 1, x].FloorType = FloorType.TempWall1;
								this.cave[pt.y + 1, x].FloorType = FloorType.TempWall1;
							}
							this.cave[pt.y, pt.x - 5].FloorType = FloorType.TempWall1;
							this.cave[pt.y, pt.x + 5].FloorType = FloorType.TempWall1;
							this.cave.place_secret_door(pt.y - 3 + (Dice.RandInt(2) << 1), pt.x - 3);
							this.cave.place_secret_door(pt.y - 3 + (Dice.RandInt(2) << 1), pt.x + 3);
							if (Dice.RandInt(3) == 1) this.cave.place_object(pt.y, pt.x - 2);
							if (Dice.RandInt(3) == 1) this.cave.place_object(pt.y, pt.x + 2);
							vault_monster(pt.y, pt.x - 2, Dice.RandInt(2));
							vault_monster(pt.y, pt.x + 2, Dice.RandInt(2));
						}
						break;
					}
				case 4:
					{
						Debug.WriteLine("Maze inside");
						// Maze inside.	
						int tmp = Dice.RandInt(4);
						if (tmp < 3)
						{	// Place a door	
							if (tmp == 1) this.cave.place_secret_door(top - 1, pt.x);
							else this.cave.place_secret_door(btm + 1, pt.x);
						}
						else
						{
							if (tmp == 3) this.cave.place_secret_door(pt.y, lft - 1);
							else this.cave.place_secret_door(pt.y, rgt + 1);
						}

						for (int y = top; y <= btm; y++)
						{
							for (int x = lft; x <= rgt; x++)
							{
								if ((x + y).IsOdd())
								{
									cave[y, x].FloorType = FloorType.TempWall1;
								}
							}
						}
						// Monsters just love mazes.		
						vault_monster(pt.y, pt.x - 5, Dice.RandInt(3));
						vault_monster(pt.y, pt.x + 5, Dice.RandInt(3));
						// Traps make them entertaining.	
						vault_trap(pt.y, pt.x - 3, 2, 8, Dice.RandInt(3));
						vault_trap(pt.y, pt.x + 3, 2, 8, Dice.RandInt(3));
						//// Mazes should have some treasure too..	
						for (int y = 0; y < 3; y++)
							this.cave.random_object(pt, 1);
						break;
					}
				case 5:
					{
						Debug.WriteLine("Four small rooms");
						// Four small rooms.	
						for (int y = top; y <= btm; y++)
						{
							this.cave[y, pt.x].FloorType = FloorType.TempWall1;
						}

						for (int x = lft; x <= rgt; x++)
						{
							cave[pt.y, x].FloorType = FloorType.TempWall1;
						}

						if (Dice.RandInt(2) == 1)
						{
							int y = Dice.RandInt(10);
							this.cave.place_secret_door(top - 1, pt.x - y);
							this.cave.place_secret_door(top - 1, pt.x + y);
							this.cave.place_secret_door(btm + 1, pt.x - y);
							this.cave.place_secret_door(btm + 1, pt.x + y);
						}
						else
						{
							int y = Dice.RandInt(3);
							this.cave.place_secret_door(pt.y + y, lft - 1);
							this.cave.place_secret_door(pt.y - y, lft - 1);
							this.cave.place_secret_door(pt.y + y, rgt + 1);
							this.cave.place_secret_door(pt.y - y, rgt + 1);
						}

						//// Treasure in each one.		
						this.cave.random_object(pt, 2 + Dice.RandInt(2));
						// Gotta have some monsters.		
						vault_monster(pt.y + 2, pt.x - 4, Dice.RandInt(2));
						vault_monster(pt.y + 2, pt.x + 4, Dice.RandInt(2));
						vault_monster(pt.y - 2, pt.x - 4, Dice.RandInt(2));
						vault_monster(pt.y - 2, pt.x + 4, Dice.RandInt(2));
						break;
					}
			}
		}

		private void vault_trap(int y, int x, int dy, int dx, int num)
		{
			this.vault_trap(new Point(x, y), dy, dx, num);
		}
		private void vault_trap(Point pt, int dy, int dx, int num)
		{
			for (int i = 0; i < num; i++)
			{
				bool flag = false;
				int count = 0;
				do
				{
					int y1 = pt.y - dy - 1 + Dice.RandInt(2 * dy + 1);
					int x1 = pt.x - dx - 1 + Dice.RandInt(2 * dx + 1);
					CaveFloor c_ptr = cave[y1, x1];
					if ((c_ptr.FloorType != FloorType.NullWall) &&
						 (c_ptr.FloorType <= FloorType.MaxCaveFloor) &&
						 (!c_ptr.HasItem))
					{
						place_trap(y1, x1, Dice.RandInt(Treasure.MAX_TRAP) - 1);
						flag = true;
					}
					count++;
				}
				while ((!flag) && (count <= 5));
			}
		}

		public void place_trap(int y, int x, int subval)
		{
			cave.t_list.Add(new InventoryItem(Treasure.OBJ_TRAP_LIST + subval));
		}
		private void vault_monster(int y, int x, int num)
		{
			this.vault_monster(new Point(x, y), num);
		}
		private void vault_monster(Point pt, int num)
		{

			for (int i = 0; i < num; i++)
			{
				this.cave.summon_monster(pt, true);
			}
		}

		/// <summary>
		/// ItemType 3 unusual rooms are cross shaped				
		/// </summary>
		private void build_type3(Point pt)
		{
			FloorType floor = FloorType.DarkFloor;
			if (dun_level <= Dice.RandInt(25))
				floor = FloorType.LightFloor;	// Floor with light	

			int tmp = 2 + Dice.RandInt(2);
			int top = pt.y - tmp;
			int btm = pt.y + tmp;
			int lft = pt.x - 1;
			int rgt = pt.x + 1;

			this.RoomAt(floor, top, btm, lft, rgt);

			tmp = 2 + Dice.RandInt(9);
			top = pt.y - 1;
			btm = pt.y + 1;
			lft = pt.x - tmp;
			rgt = pt.x + tmp;

			this.RoomAt(floor, top, lft, btm, rgt);

			// Special features.			
			switch (Dice.RandInt(4))
			{
				case 1:
					{
						// Large middle pillar		
						for (int y = pt.y - 1; y <= pt.y + 1; y++)
						{
							for (int x = pt.x - 1; x <= pt.x + 1; x++)
							{
								this.cave[y, x].FloorType = FloorType.TempWall1;
							}
						}
						break;
					}
				case 2:
					{
						// Inner treasure vault		
						for (int y = pt.y - 1; y <= pt.y + 1; y++)
						{
							cave[y, pt.x - 1].FloorType = FloorType.TempWall1;
							cave[y, pt.x + 1].FloorType = FloorType.TempWall1;
						}
						cave[pt.y - 1, pt.x].FloorType = FloorType.TempWall1;
						cave[pt.y + 1, pt.x].FloorType = FloorType.TempWall1;

						tmp = Dice.RandInt(4);	// Place a door	
						if (tmp < 3)
							this.cave.place_secret_door(pt.y - 3 + (tmp << 1), pt.x);
						else
							this.cave.place_secret_door(pt.y, pt.x - 7 + (tmp << 1));

						//   // Place a treasure in the vault		
						this.cave.place_object(pt);

						//TODO
						//   // Let's guard the treasure well.	
						//   vault_monster(Point.y, Point.x, 2+Dice.RandInt(2));
						//   // Traps naturally			
						//   vault_trap(Point.y, Point.x, 4, 4, 1+Dice.RandInt(3));
						break;
					}
				case 3:
					{

						if (Dice.RandInt(3) == 1)
						{
							cave[pt.y - 1, pt.x - 2].FloorType = FloorType.TempWall1;
							cave[pt.y + 1, pt.x - 2].FloorType = FloorType.TempWall1;
							cave[pt.y - 1, pt.x + 2].FloorType = FloorType.TempWall1;
							cave[pt.y + 1, pt.x + 2].FloorType = FloorType.TempWall1;
							cave[pt.y - 2, pt.x - 1].FloorType = FloorType.TempWall1;
							cave[pt.y - 2, pt.x + 1].FloorType = FloorType.TempWall1;
							cave[pt.y + 2, pt.x - 1].FloorType = FloorType.TempWall1;
							cave[pt.y + 2, pt.x + 1].FloorType = FloorType.TempWall1;
							if (Dice.RandInt(3) == 1)
							{
								this.cave.place_secret_door(pt.y, pt.x - 2);
								this.cave.place_secret_door(pt.y, pt.x + 2);
								this.cave.place_secret_door(pt.y - 2, pt.x);
								this.cave.place_secret_door(pt.y + 2, pt.x);
							}
						}
						else if (Dice.RandInt(3) == 1)
						{
							cave[pt.y, pt.x].FloorType = FloorType.TempWall1;
							cave[pt.y - 1, pt.x].FloorType = FloorType.TempWall1;
							cave[pt.y + 1, pt.x].FloorType = FloorType.TempWall1;
							cave[pt.y, pt.x - 1].FloorType = FloorType.TempWall1;
							cave[pt.y, pt.x + 1].FloorType = FloorType.TempWall1;
						}
						else if (Dice.RandInt(3) == 1)
						{
							cave[pt.y, pt.x].FloorType = FloorType.TempWall1;
						}
						break;
					}
				case 4:
					// "empty inner room variation");
					break;
			}
		}

		private void place_streamer(FloorType floorType, int treas_chance)
		{

			// Choose starting point and direction		
			int y = (cur_height / 2) + 11 - Dice.RandInt(23);
			int x = (cur_width / 2) + 16 - Dice.RandInt(33);

			Direction dir = (Direction)Dice.RandInt(9);	// Number 1-4, 6-9	


			// Place streamer into dungeon			
			int t1 = 2 * CaveFactory.DUN_STR_RNG + 1;	// Constants	
			int t2 = CaveFactory.DUN_STR_RNG + 1;
			do
			{
				for (int i = 0; i < DUN_STR_DEN; i++)
				{
					int ty = y + Dice.RandInt(t1) - t2;
					int tx = x + Dice.RandInt(t1) - t2;
					if (this.cave.in_bounds(ty, tx))
					{
						CaveFloor c_ptr = this.cave[ty, tx];
						if (c_ptr.FloorType == FloorType.GraniteWall)
						{
							c_ptr.FloorType = floorType;
							if (Dice.RandInt(treas_chance) == 1)
								this.cave.place_gold(ty, tx);
						}
					}
				}
			}
			while (mmove(dir, ref x, ref y));

		}

		private void build_tunnel(Point firstPoint, Point secondPoint)
		{
			this.build_tunnel(firstPoint.y, firstPoint.x, secondPoint.y, secondPoint.x);
		}

		private void build_tunnel(int row1, int col1, int row2, int col2)
		{
			long ticks = DateTime.Now.Ticks;
			try
			{
				/* Main procedure for Tunnel			*/
				/* Note: 9 is a temporary value		*/
				bool stop_flag = false;
				bool door_flag = false;
				int main_loop_count = 0;
				int start_row = row1;
				int start_col = col1;
				int row_dir, col_dir;
				correct_dir(out row_dir, out col_dir, row1, col1, row2, col2);


				Stack<Point> tunstk = new Stack<Point>();
				Stack<Point> wallstk = new Stack<Point>();

				do
				{
					/* prevent infinite loops, just in case */
					main_loop_count++;
					if (main_loop_count > 2000)
						stop_flag = true;

					if (Dice.RandInt(100) > DUN_TUN_CHG)
					{
						if (Dice.RandInt(DUN_TUN_RND) == 1) rand_dir(out row_dir, out col_dir);
						else correct_dir(out row_dir, out col_dir, row1, col1, row2, col2);
					}
					int tmp_row = row1 + row_dir;
					int tmp_col = col1 + col_dir;
					while (!this.cave.in_bounds(tmp_row, tmp_col))
					{
						if (Dice.RandInt(DUN_TUN_RND) == 1) rand_dir(out row_dir, out col_dir);
						else correct_dir(out row_dir, out col_dir, row1, col1, row2, col2);
						tmp_row = row1 + row_dir;
						tmp_col = col1 + col_dir;
					}

					CaveFloor c_ptr = cave[tmp_row, tmp_col];
					if (c_ptr.FloorType == FloorType.NullWall)
					{
						row1 = tmp_row;
						col1 = tmp_col;

						if (tunstk.Count < 1000)
						{
							tunstk.Push(new Point(col1, row1));
						}
						door_flag = false;
					}
					else if (c_ptr.FloorType == FloorType.TempWall2)
					{
						;   /* do nothing */
					}
					else if (c_ptr.FloorType == FloorType.GraniteWall)
					{
						row1 = tmp_row;
						col1 = tmp_col;
						if (wallstk.Count < 1000)
						{
							wallstk.Push(new Point(col1, row1));
						}

						for (int i = row1 - 1; i <= row1 + 1; i++)
						{
							for (int j = col1 - 1; j <= col1 + 1; j++)
							{
								if (this.cave.in_bounds(i, j))
								{
									CaveFloor d_ptr = cave[i, j];
									/* values 11 and 12 are impossible here, place_streamer
										is never run before build_tunnel */
									if (d_ptr.FloorType == FloorType.GraniteWall)
										d_ptr.FloorType = FloorType.TempWall2;
								}
							}
						}
					}
					else if (c_ptr.FloorType == FloorType.Corridor || c_ptr.FloorType == FloorType.BlockedFloor)
					{
						row1 = tmp_row;
						col1 = tmp_col;
						if (!door_flag)
						{
							if (this.doorstk.Count < 100)
							{
								this.doorstk.Push(new Point(col1, row1));
							}
							door_flag = true;
						}
						if (Dice.RandInt(100) > DUN_TUN_CON)
						{
							/* make sure that tunnel has gone a reasonable distance
						  before stopping it, this helps prevent isolated rooms */
							tmp_row = row1 - start_row;
							if (tmp_row < 0) tmp_row = -tmp_row;
							tmp_col = col1 - start_col;
							if (tmp_col < 0) tmp_col = -tmp_col;

							if (tmp_row > 10 || tmp_col > 10) stop_flag = true;
						}
					}
					else  /* c_ptr->fval != NULL, TMP2, GRANITE, CORR */
					{
						row1 = tmp_row;
						col1 = tmp_col;
					}
				}
				while (((row1 != row2) || (col1 != col2)) && (!stop_flag));


				while (tunstk.Count > 0)
				{
					Point pt = tunstk.Pop();
					CaveFloor d_ptr = cave[pt];
					d_ptr.FloorType = FloorType.Corridor;
				}

				while (wallstk.Count > 0)
				{
					Point pt = wallstk.Pop();
					CaveFloor c_ptr = cave[pt];
					if (c_ptr.FloorType == FloorType.TempWall2)
					{
						if (Dice.RandInt(100) < DUN_TUN_PEN)
						{
							this.cave.place_door(pt);
						}
						else
						{
							/* these have to be doorways to rooms */
							c_ptr.FloorType = FloorType.Corridor;
						}
					}
				}
				ticks = DateTime.Now.Ticks - ticks;
			}
			catch (Exception err)
			{
				Debug.WriteLine("exception:" + err);
			}
			Debug.WriteLine("Tunnel Elapsed:" + TimeSpan.FromTicks(ticks));
		}

		private void build_store(int storeNum, int y, int x)
		{
			int yval = y * 10 + 5;
			int xval = x * 16 + 16;
			int y_height = yval - Dice.RandInt(3);
			int y_depth = yval + Dice.RandInt(4);
			int x_left = xval - Dice.RandInt(6);
			int x_right = xval + Dice.RandInt(6);

			for (y = y_height; y <= y_depth; y++)
			{
				for (x = x_left; x <= x_right; x++)
				{
					cave[y, x].FloorType = FloorType.BoundaryWall;
				}
			}

			int tmp = Dice.RandInt(4);
			if (tmp < 3)
			{
				y = Dice.RandInt(y_depth - y_height) + y_height - 1;
				if (tmp == 1) x = x_left;
				else x = x_right;
			}
			else
			{
				x = Dice.RandInt(x_right - x_left) + x_left - 1;
				if (tmp == 3) y = y_depth;
				else y = y_height;
			}

			CaveFloor c_ptr = cave[y, x];
			c_ptr.FloorType = FloorType.Corridor;
			cave.t_list.Add(new InventoryItem(Treasure.OBJ_STORE_DOOR + (storeNum - 1))
			{
				X = x,
				Y = y,
			});

		}

		#endregion

		private void try_door(int y, int x)
		{

			if ((this.cave[y, x].FloorType == FloorType.Corridor) &&
				  (Dice.RandInt(100) > DUN_TUN_JCT) &&
				  this.cave.next_to(y, x))
			{
				this.cave.place_door(new Point(x, y));
			}

		}

		public DungeonLevel Create(int level)
		{
			long ticks = DateTime.Now.Ticks;
			try
			{
				this.cave = new DungeonLevel(level);

				if (dun_level == 0)
				{
					GenerateTown();
				}
				else
				{
					GenerateCave();
				}

				ticks = DateTime.Now.Ticks - ticks;

			}
			catch (Exception err)
			{
				Debug.WriteLine("exception:" + err);
				this.cave = null;
			}

			Debug.WriteLine("Elapsed:" + TimeSpan.FromTicks(ticks));
			return this.cave;
		}

		private void GenerateTown()
		{
			int[] rooms = { 1, 2, 3, 4, 5, 6 };

			int l = 6;
			for (int y = 0; y < 2; y++)
			{
				for (int x = 0; x < 3; x++)
				{
					int k = Dice.RandInt(l) - 1;
					build_store(rooms[k], y, x);
					for (int m = k; m < l - 1; m++)
					{
						rooms[m] = rooms[m + 1];
					}
					l--;
				}
			}

			fill_cave(FloorType.DarkFloor);
			/* make stairs before reset_seed, so that they don't move around */
			place_boundary();
			this.cave.place_stairs(StairType.Down, 1, 0);
			/* Set up the character co-ords, used by alloc_monster below */

			Point pt = new_spot();
			this.cave.X = pt.x;
			this.cave.Y = pt.y;

			bool nightFlag = ((0x1 & (this.Turn() / 5000)) != 0);

			for (int y = 0; y < cur_height; y++)
			{
				for (int x = 0; x < cur_width; x++)
				{
					CaveFloor c_ptr = this.cave[y, x];

					if (nightFlag)
					{
						if (c_ptr.FloorType != FloorType.DarkFloor)
							c_ptr.PermLight = true;
					}
					else
					{
						c_ptr.PermLight = true;
					}
				}
			}

			if (nightFlag) this.cave.alloc_monster(MIN_MALLOC_TN, 3, true);
			else this.cave.alloc_monster(MIN_MALLOC_TD, 3, true);

		}


		private void GenerateCave()
		{
			int row_rooms = 2 * (cur_height / DungeonLevel.SCREEN_HEIGHT);
			int col_rooms = 2 * (cur_width / DungeonLevel.SCREEN_WIDTH);
			bool[,] room_map = new bool[20, 20];
			for (int i = 0; i < row_rooms; i++)
			{
				for (int j = 0; j < col_rooms; j++)
				{
					room_map[i, j] = false;
				}
			}


			int k = Dice.RandNor(DUN_ROO_MEA, 2);
			for (int i = 0; i < k; i++)
			{
				room_map[Dice.RandInt(row_rooms) - 1, Dice.RandInt(col_rooms) - 1] = true;
			}

			k = 0;
			List<Point> loc = new List<Point>();
			for (int i = 0; i < row_rooms; i++)
			{
				for (int j = 0; j < col_rooms; j++)
				{
					if (room_map[i, j])
					{
						loc.Add(new Point()
						{
							y = i * (DungeonLevel.SCREEN_HEIGHT >> 1) + DungeonLevel.QUART_HEIGHT,
							x = j * (DungeonLevel.SCREEN_WIDTH >> 1) + DungeonLevel.QUART_WIDTH,
						});

						if (dun_level > Dice.RandInt(DUN_UNUSUAL))
						{
							int tmp = Dice.RandInt(3);
							if (tmp == 1) build_type1(loc.Last());
							else if (tmp == 2) build_type2(loc.Last());
							else build_type3(loc.Last());
						}
						else
						{
							build_room(loc.Last());
						}

					}
				}
			}


			loc = loc.OrderBy(i => Dice.RandInt(loc.Count)).ToList();

			/* move zero entry to k, so that can call build_tunnel all k times */
			loc.Add(loc.First());
			this.doorstk.Clear();

			for (int i = 0; i < loc.Count - 1; i++)
			{
				build_tunnel(loc[i + 1], loc[i]);
			}

			fill_cave(FloorType.GraniteWall);

			for (int i = 0; i < DUN_STR_MAG; i++)
				place_streamer(FloorType.MagamaWall, DUN_STR_MC);

			for (int i = 0; i < DUN_STR_QUA; i++)
				place_streamer(FloorType.QuartzWall, DUN_STR_QC);

			place_boundary();

			/* Place intersection doors	*/
			while (doorstk.Count > 0)
			{
				Point pt = doorstk.Pop();
				try_door(pt.y, pt.x - 1);
				try_door(pt.y, pt.x + 1);
				try_door(pt.y - 1, pt.x);
				try_door(pt.y + 1, pt.x);
			}

			int alloc_level = (dun_level / 3);
			if (alloc_level < 2) alloc_level = 2;
			else if (alloc_level > 10) alloc_level = 10;

			this.cave.place_stairs(StairType.Down, Dice.RandInt(2) + 2, 3);
			this.cave.place_stairs(StairType.Up, Dice.RandInt(2), 3);
			/* Set up the character co-ords, used by alloc_monster, place_win_monster */

			Point charPt = new_spot();
			cave.X = charPt.x;
			cave.Y = charPt.y;
			//TODO:
			//alloc_monster((Dice.RandInt(8) + MIN_MALLOC_LEVEL + alloc_level), 0, TRUE);
			//alloc_object(set_corr, 3, Dice.RandInt(alloc_level));
			//alloc_object(set_room, 5, randnor(TREAS_ROOM_ALLOC, 3));
			//alloc_object(set_floor, 5, randnor(TREAS_ANY_ALLOC, 3));
			//alloc_object(set_floor, 4, randnor(TREAS_GOLD_ALLOC, 3));
			//alloc_object(set_floor, 1, Dice.RandInt(alloc_level));
			//if (dun_level >= WIN_MON_APPEAR) place_win_monster();
		}

		private void place_boundary()
		{
			int hgt = this.cave.Max(c => c.Y);
			int wdt = this.cave.Max(x => x.X);

			foreach (CaveFloor flr in this.cave.Where(c => (c.X == 0 || c.X == wdt) || (c.Y == 0 || c.Y == hgt)))
			{
				flr.FloorType = FloorType.BoundaryWall;
			}
		}

		private void fill_cave(FloorType floorType)
		{
			int hgt = this.cave.Max(c => c.Y);
			int wdt = this.cave.Max(x => x.X);

			foreach (CaveFloor flr in this.cave.Where(c => (c.X.ExclusiveBetween(0, wdt) && c.Y.ExclusiveBetween(0, hgt)) &&
																								 (c.FloorType == FloorType.NullWall) ||
																								 (c.FloorType == FloorType.TempWall1) ||
																								 (c.FloorType == FloorType.TempWall2)))
			{
				flr.FloorType = floorType;
			}

		}


		private bool mmove(Direction dir, ref int x, ref int y)
		{
			int yNew = -1;
			int xNew = -1;

			if (dir.Has(Direction.South)) yNew = y + 1;
			if (dir.Has(Direction.North)) yNew = y - 1;
			if (dir.Has(Direction.East)) xNew = x + 1;
			if (dir.Has(Direction.West)) xNew = x - 1;

			if (this.cave.in_bounds(yNew, xNew))
			{
				y = yNew;
				x = xNew;
				return true;
			}
			return false;
		}

		private void rand_dir(out int row, out int col)
		{

			int tmp = Dice.RandInt(4);
			if (tmp < 3)
			{
				col = 0;
				row = -3 + (tmp << 1); // tmp=1 . *dir.y=-1; tmp=2 . *dir.y=1 
			}
			else
			{
				row = 0;
				col = -7 + (tmp << 1); // tmp=3 . *xDic=-1; tmp=4 . *xDic=1 
			}

		}

		private void correct_dir(out int rdir, out int cdir, int y1, int x1, int y2, int x2)
		{

			if (y1 < y2) rdir = 1;
			else if (y1 == y2) rdir = 0;
			else rdir = -1;

			if (x1 < x2) cdir = 1;
			else if (x1 == x2) cdir = 0;
			else cdir = -1;

			if ((rdir != 0) && (cdir != 0))
			{
				if (Dice.RandInt(2) == 1) rdir = 0;
				else cdir = 0;
			}
		}
		private Point new_spot()
		{
			Point pt = new Point();
			CaveFloor c_ptr = null;
			do
			{
				pt.y = Dice.RandInt(cur_height - 2);
				pt.x = Dice.RandInt(cur_width - 2);
				c_ptr = cave[pt];
			}
			while (c_ptr.FloorType >= FloorType.MinClosedSpace || (c_ptr.HasCreature) || (c_ptr.HasItem));
			return pt;
		}

		#endregion Methods

		#region Events


		#endregion Events

		#region Properties

		public Func<int> Turn { get; set; }

		#endregion Properties

	}
}
